/**
 *  EKology - An Ecosystem Simulation Framework
 *  Copyright (C) 2008  Aarón Tavío - aaron.tavio at gmail.com - ULPGC
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 * 
 */

package net.ekology.core;

import net.ekology.core.datatypes.*;
import net.ekology.ekosystem.*;

import java.util.Hashtable;
import java.io.*;


/**
 * A signal monitor
 * <p>
 * Can be configured to store signal values, from the ecosystem into the database
 * 
 * @author Aarón Tavío - aaron.tavio at gmail.com
 * @version 1.0.0 - 20081019-1556
 */
public class Monitor implements Serializable {
    private String sProject;
    private String sSession;
    private Hashtable <String, Signal> htSignal;
    private boolean bIsEnabled;
    private EKosystem oEKosystem;
    private DatabaseManager oDBM;
    
    
    
    public Monitor(EKosystem oEKosystem, DatabaseManager oDatabaseManager) {
        this.oEKosystem = oEKosystem;
        this.oDBM = oDatabaseManager;
        
        reset();
    }
    
    
    /**
     * Adds elapsed time to each Signal being monitored
     * 
     * @param iMiliseconds the amount of milliseconds
     * @return <code>true</code> if a breakpoint is triggered, else <code>false</code>
     */
    public boolean addElapsedTime(int iMiliseconds) {
        long lEKTimeElapsed;
        int iSignalID;
        boolean bBreakpointTriggered;
        ValueData oValueData;
        
        bBreakpointTriggered = false;
        if (bIsEnabled) {
            lEKTimeElapsed = Math.round(iMiliseconds*oEKosystem.getTimeFactor());
        
            for(Signal oSignal: htSignal.values()) {
                if (oSignal.addElapsedTime(lEKTimeElapsed)) { // Signal triggered
                    oValueData = oEKosystem.getBiotope().getValueData(oSignal.getName());
                    
                    if (oSignal.getStatus() == SignalStatusType.BREAKPOINT) { // check breakpoint activation
                        if (oSignal.getType() == SignalType.INTEGER && (Integer)oValueData.oValue>0) bBreakpointTriggered = true;
                    }
                    else { // register the signal
                        iSignalID = oDBM.getSignalID(sProject,sSession,oSignal.getName());
                        if (!oDBM.hasSignal(iSignalID)) {
                            oDBM.addSignal(oDBM.getSessionID(sProject, sSession), new SignalData(oSignal.getName(),oSignal.getType()));
                            iSignalID = oDBM.getSignalID(sProject,sSession,oSignal.getName());
                        }
                        oDBM.addValue(iSignalID, oValueData);
                    }
                }
            }
        }
        
        return bBreakpointTriggered;
    }
    
    
    public ProjectData getProjectData() {
        ProjectData oResult;
        
        oResult = null;
        if (sProject != null) oResult = oDBM.getProject(oDBM.getProjectID(sProject));
                
        return oResult;
    }
    
    
    public void setProjectData(ProjectData oProjectData) {
        Signal[] aSignal;
        String[] aSignalName;
        SignalData oSignalData;
        
        if (sProject == null) {
            sProject = oProjectData.sName;
            
            aSignalName = oEKosystem.getBiotope().getSignalNames();
            aSignal = new Signal[aSignalName.length];
            
            for(int i=0; i<aSignalName.length; i++) {
                oSignalData = oEKosystem.getBiotope().getSignalData(aSignalName[i]);
                aSignal[i] = new Signal(oSignalData.sName,oSignalData.oType,0);
            }
            setSignals(aSignal);
            oDBM.connect();
        }
            
        if (oDBM.hasProject(oDBM.getProjectID(sProject))) oDBM.setProject(oProjectData);
        else oDBM.addProject(oProjectData);
    }
    
    
    public SessionData getSessionData() {
        SessionData oResult;
        
        oResult = null;
        if (sSession != null) oResult = oDBM.getSession(oDBM.getSessionID(sProject,sSession));
                
        return oResult;
    }
    
    
    public void setSessionData(SessionData oSessionData) {
        int iSessionID;
        
        if (sSession == null) sSession = oSessionData.sName;
        
        iSessionID = oDBM.getSessionID(sProject,sSession);
        if (oDBM.hasSession(iSessionID)) oDBM.setSession(iSessionID,oSessionData);
        else oDBM.addSession(oDBM.getProjectID(sProject),oSessionData);
    }
    
    
    public Signal[] getSignals() {
        Signal[] aResult;
        
        aResult = htSignal.values().toArray(new Signal[0]);
        
        return aResult;
    }
    
    
    public void setSignals(Signal[] aSignal) {
        for(Signal oSignal: aSignal) {
            htSignal.put(oSignal.getName(), oSignal);
        }
    }
    
    
    public boolean getMonitoring() {
        return bIsEnabled;
    }
    
    
    public void setMonitoring(boolean bIsEnabled) {
        this.bIsEnabled = bIsEnabled;
    }
    
    
    public void reset() {
        this.sProject = null;
        this.sSession = null;
        this.htSignal = new Hashtable<String,Signal>();
        this.bIsEnabled = false;
        this.oDBM.disconnect();
    }
    
    
    /**
     * Use to deserialize the object
     * 
     * @param in the input stream
     * @throws java.io.IOException
     * @throws java.lang.ClassNotFoundException
     */
    private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
        in.defaultReadObject();
        if (sProject!=null) oDBM.connect();
    }
    
}
